/*
 * TeleStax, Open Source Cloud Communications  Copyright 2012. 
 * and individual contributors
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.mobicents.protocols.ss7.sccp.impl;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.mobicents.protocols.ss7.Util;
import org.mobicents.protocols.ss7.mtp.Mtp3;
import org.mobicents.protocols.ss7.mtp.Mtp3PausePrimitive;
import org.mobicents.protocols.ss7.mtp.Mtp3ResumePrimitive;
import org.mobicents.protocols.ss7.mtp.Mtp3StatusCause;
import org.mobicents.protocols.ss7.mtp.Mtp3StatusPrimitive;
import org.mobicents.protocols.ss7.mtp.Mtp3TransferPrimitive;
import org.mobicents.protocols.ss7.mtp.Mtp3TransferPrimitiveFactory;
import org.mobicents.protocols.ss7.mtp.Mtp3UserPartBaseImpl;

/**
 * @author abhayani
 * @author baranowb
 * @author sergey vetyutnev
 */
public class Mtp3UserPartImpl extends Mtp3UserPartBaseImpl {

//	protected ConcurrentLinkedQueue<byte[]> readFrom;
//	protected ConcurrentLinkedQueue<byte[]> writeTo;
	
	private Mtp3UserPartImpl otherPart;
	private ArrayList<Mtp3TransferPrimitive> messages = new ArrayList<Mtp3TransferPrimitive>();
	
	protected boolean saveTrafficInFile = false;

	public Mtp3UserPartImpl() {
		try {
			this.start();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public void setOtherPart(Mtp3UserPartImpl otherPart) {
		this.otherPart = otherPart;
	}

	public void sendMessage(Mtp3TransferPrimitive msg) throws IOException {
		if (saveTrafficInFile) {
			try {
				// pcap version
				// m3ua part
				byte[] sccpPart = msg.getData();
				ByteArrayOutputStream stm1 = new ByteArrayOutputStream();
				stm1.write(1); // m3ua version - release 1
				stm1.write(0); // m3ua reserved
				stm1.write(1); // m3ua message class - Transfer message
				stm1.write(1); // m3ua Message type - Payload data

				int lenb = sccpPart.length + 16;
				int lena = lenb + 8;
				int m3uaPad = lena % 4;
				if (m3uaPad > 0)
					m3uaPad = 4 - m3uaPad;
				lena += m3uaPad;

				stm1.write(0); // m3ua Payload data length
				stm1.write(0);
				stm1.write(lena / 256);
				stm1.write(lena % 256);				

				stm1.write(2); // Parameter tag: protocol data
				stm1.write(0x10);

				stm1.write(lenb / 256); // protocol data length
				stm1.write(lenb % 256);				

				stm1.write(0); // m3ua opc
				stm1.write(0);
				stm1.write(msg.getOpc() / 256);
				stm1.write(msg.getOpc() % 256);				

				stm1.write(0); // m3ua dpc
				stm1.write(0);
				stm1.write(msg.getDpc() / 256);
				stm1.write(msg.getDpc() % 256);				

				stm1.write(msg.getSi());
				stm1.write(msg.getNi());
				stm1.write(msg.getMp());
				stm1.write(msg.getSls());
				
				stm1.write(sccpPart);

				for (int i1 = 0; i1 < m3uaPad; i1++) {
					stm1.write(0);
				}

				byte[] m3uaPart = stm1.toByteArray();

				// sctp part
				ByteArrayOutputStream stm2 = new ByteArrayOutputStream();
				stm2.write(1); // source port
				stm2.write(1);
				stm2.write(2); // dest port
				stm2.write(2);
				stm2.write(3); // verification tag
				stm2.write(3);
				stm2.write(3);
				stm2.write(3);
				stm2.write(4); // checksum - not really calculated
				stm2.write(4);
				stm2.write(4);
				stm2.write(4);

				int lenc = m3uaPart.length + 16;
				stm2.write(0); // chunk type - data
				stm2.write(3); // chunk flags
				stm2.write(lenc / 256); // chunk length
				stm2.write(lenc % 256);

				stm2.write(0); // TSN
				stm2.write(0);
				stm2.write(0);
				stm2.write(0);

				stm2.write(0); // stream identifier
				stm2.write(3);

				stm2.write(11); // stream sequence number
				stm2.write(11);

				stm2.write(0); // protocol identifier - m3ua
				stm2.write(0);
				stm2.write(0);
				stm2.write(3);

				stm2.write(m3uaPart);

				byte[] sctpPart = stm2.toByteArray();

				// ip V4 part
				ByteArrayOutputStream stm3 = new ByteArrayOutputStream();
				int lend = sctpPart.length + 20;
				stm3.write(0x45); // version 4 + headerLen=20
				stm3.write(0xe0); // dscp + ecn
				stm3.write(lend / 256); // total length
				stm3.write(lend % 256);

				stm3.write(10); // identification
				stm3.write(11);
				stm3.write(0); // flags + fragment offset
				stm3.write(0);

				stm3.write(0x40); // time to live
				stm3.write(0x84); // protocol: sctp
				stm3.write(0); // checksum: not really checked
				stm3.write(0);

				stm3.write(1); // source ip address
				stm3.write(2);
				stm3.write(3);
				stm3.write(4);

				stm3.write(4); // dest ip address
				stm3.write(3);
				stm3.write(2);
				stm3.write(1);

				stm3.write(sctpPart);

				byte[] ipPart = stm3.toByteArray();
				
				// calculating ip header checksum
				int ix = 0;
				for (int i1 = 0; i1 < 10; i1++) {
					int ii1 = ipPart[i1 * 2] & 0xFF;
					int ii2 = ipPart[i1 * 2 + 1] & 0xFF;
					ix += (ii1 << 8) + ii2;
				}
				int iy = ix & 0xFFFF;
				int iz = iy ^ 0xFFFF;
				ipPart[10] = (byte) (iz / 256);
				ipPart[11] = (byte) (iz % 256);

				// Ethernet part
				ByteArrayOutputStream stm4 = new ByteArrayOutputStream();
				stm4.write(8); // source
				stm4.write(8);
				stm4.write(8);
				stm4.write(8);
				stm4.write(8);
				stm4.write(8);

				stm4.write(6); // destination
				stm4.write(6);
				stm4.write(6);
				stm4.write(6);
				stm4.write(6);
				stm4.write(6);

				stm4.write(8); // type: ip
				stm4.write(0);

				stm4.write(ipPart);

				byte[] ethPart = stm4.toByteArray();
				
				// pcap header 
				FileOutputStream fs = new FileOutputStream(Util.getTmpTestDir()+File.separator+"MsgLog.pcap", true);
				int ln = ethPart.length;

				fs.write(0);
				fs.write(0);
				fs.write(0);
				fs.write(0);

				fs.write(0);
				fs.write(0);
				fs.write(0);
				fs.write(0);
				
				fs.write(ln % 256);
				fs.write(ln / 256);
				fs.write(0);
				fs.write(0);

				fs.write(ln % 256);
				fs.write(ln / 256);
				fs.write(0);
				fs.write(0);

				// pcap data
				fs.write(ethPart);
				fs.close();

				// MsgLog.txt version
//				byte[] txData = msg.encodeMtp3();
//				FileOutputStream fs = new FileOutputStream("MsgLog.txt", true);
//				int ln = txData.length;
//				fs.write(ln & 0xFF);
//				fs.write(ln >> 8);
//				fs.write(txData);
//				fs.close();
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		if (this.otherPart != null)
			this.otherPart.sendTransferMessageToLocalUser(msg, msg.getSls());
		else
			this.messages.add(msg);
	}

	public void sendTransferMessageToLocalUser(int opc, int dpc, byte[] data) {
		int si = Mtp3._SI_SERVICE_SCCP;
		int ni = 2;
		int mp = 0;
		int sls = 0;
		Mtp3TransferPrimitiveFactory factory = this.getMtp3TransferPrimitiveFactory();
		Mtp3TransferPrimitive msg = factory.createMtp3TransferPrimitive(si, ni, mp, opc, dpc, sls, data);
		int seqControl = 0;
		this.sendTransferMessageToLocalUser(msg, seqControl);
	}	

	public void sendPauseMessageToLocalUser(int affectedDpc) {
		Mtp3PausePrimitive msg = new Mtp3PausePrimitive(affectedDpc);
		this.sendPauseMessageToLocalUser(msg);
	}	

	public void sendResumeMessageToLocalUser(int affectedDpc) {
		Mtp3ResumePrimitive msg = new Mtp3ResumePrimitive(affectedDpc);
		this.sendResumeMessageToLocalUser(msg);
	}	

	public void sendStatusMessageToLocalUser(int affectedDpc, Mtp3StatusCause cause, int congestionLevel) {
		Mtp3StatusPrimitive msg = new Mtp3StatusPrimitive(affectedDpc, cause, congestionLevel);
		this.sendStatusMessageToLocalUser(msg);
	}

	public List<Mtp3TransferPrimitive> getMessages() {
		return messages;
	}

	@Override
	public int getMaxUserDataLength(int dpc) {
		return 1000;
	}
}

